#ifndef FIELD_H_
#define FIELD_H_

#include "types.h"
#include "BigInt.h"

namespace P
{
	__device__ extern uint256_t *MODULUS;
	__device__ extern uint256_t *EDWARDS_A;
    __device__ extern uint256_t *EDWARDS_D;
	constexpr uint64_t INV = 725501752471715839ULL;
};

//Field
class Field
{
public:
	__device__ static Field zero();
	__device__ static Field one();
	__device__ static Field create(Mode mode, uint256_t v);
	__device__ static Field constant(uint256_t v);
	__device__ static Field fromBitsLe(const BitArray& bits);
	__device__ static Field fromBitsBe(const BitArray& bits);
	__device__ static Field from_boolean(Boolean b);
	
	__device__ Field();

	__device__ CuString toString() const;
	__device__ bool is_constant() const;

	__device__ Field operator+(const Field& other) const;
	__device__ Field operator-(const Field& other) const;
	__device__ Field operator*(const Field& other) const;
	
	__device__ bool operator==(const Field& other) const;
	__device__ bool operator!=(const Field& other) const;

	__device__ Field inverse() const;
	__device__ Boolean msb();

	__device__ BitArray toBitsLe() const;
	__device__ BitArray toBitsBe() const;

	uint256_t b;
	Mode mode;
};
__device__ CuDebug operator<<(CuDebug dbg, const Field& message);

//Integer
typedef int256_t cpp_int;
class Integer
{
public:
	__device__ static Integer zero(int type);
	__device__ static Integer create(int type, Mode mode, cpp_int value);
	__device__ static Integer constant(int type, cpp_int value);
	__device__ static Integer fromField(int type, const Field& f);
	__device__ static Integer fromBitsLe(int type, const BitArray& f);

	__device__ Integer();

	__device__ CuString toString() const;

	__device__ Integer operator~() const;

	__device__ Integer operator+(const Integer& other) const;
	__device__ Integer operator-(const Integer& other) const;
	__device__ Integer operator*(const Integer& other) const;
	__device__ Integer operator/(const Integer& other) const;
	__device__ Integer operator%(const Integer& other) const;
	
	__device__ bool operator==(const Integer& other) const;
	__device__ bool operator<(const Integer& other) const;

	__device__ Integer wrapping_shl(const Integer& other) const;
	__device__ Integer wrapping_shr(const Integer& other) const;

	__device__ bool is_constant() const;
	__device__ bool is_signed() const;
	__device__ Boolean msb() const;

	__device__ BitArray toBitsLe() const;
	__device__ Field toField() const;
	__device__ cpp_int value() const;
	
	__device__ Mode mode() const;
	__device__ void setMode(Mode mode);
	__device__ int type() const;
	__device__ int bitSize() const;

	__device__ Integer cast_as_dual() const;
	__device__ Integer min() const;
	__device__ Integer max() const;

protected:
	__device__ void normalize();

	Mode m_mode;
	int m_type;
	cpp_int m_value;
};
__device__ CuDebug operator<<(CuDebug dbg, const Integer& i);

class Group
{
public:
	static Group zero();

	__device__ Group();
	__device__ bool is_constant() const;
	__device__ bool is_zero() const;

	Field x;
	Field y;
};

__device__ Field booleanToField(Boolean b);
__device__ Integer toInteger(const Field &f,int type);
__device__ int integerDualType(int type);
__device__ cpp_int integerMin(int type);
__device__ cpp_int integerMax(int type);

#endif // ! FIELD_H_
